package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.UserUtil;
import org.oslims.client.service.RequestServiceAsync;
import org.oslims.client.service.UserServiceAsync;
import org.oslims.client.uicomponents.FileDownloadCell;
import org.oslims.client.uicomponents.SelectionColumn;
import org.oslims.client.uicomponents.SortableHeader;
import org.oslims.client.uicomponents.WidgetUtils;
import org.oslims.client.uicomponents.editbox.EditRequestBox;
import org.oslims.client.view.impl.RequestEditorViewImpl;
import org.oslims.domain.Customer;
import org.oslims.domain.File;
import org.oslims.domain.LabResponsible;
import org.oslims.domain.Report;
import org.oslims.domain.Request;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.validation.client.InvalidConstraint;
import com.google.gwt.validation.client.interfaces.IValidator;
import com.google.gwt.view.client.ListView;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.ListView.Delegate;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = RequestEditorViewImpl.class)
public class RequestPresenter extends BasePresenter<RequestEditorViewImpl, MainEventBus> {

	private RequestServiceAsync requestService;
	private UserServiceAsync userService;

	private IValidator<Request> requestValidator;
	private List<Customer> customerList;
	private List<LabResponsible> labResponsibleList;

	private String currentSortProperty = "code";
	private Boolean currentSortDesc = true;

	private DockLayoutPanel mainWidget;

	public RequestPresenter() {
		requestValidator = GWT.create(Request.class);
		customerList = new ArrayList<Customer>();
		labResponsibleList = new ArrayList<LabResponsible>();
	}

	public void onShowRequestManagement(DockLayoutPanel mainWidget) {

		WidgetUtils.removeCenterWidget(mainWidget);

		mainWidget.add(view.getViewWidget());
		this.mainWidget = mainWidget;

		refreshChoices();
		refreshData();
	}

	public void bind() {

		bindUIButtons();

		bindLabResponsibleBox();

		bindEditReportBox();

		bindEditBox();

		addColumns();

		setDelegate();

	}

	private void setDelegate() {
		view.getTable().setDelegate(new Delegate<Request>() {

			@Override
			public void onRangeChanged(ListView<Request> listView) {

				final Range r = listView.getRange();

				requestService.getAllRequest(r.getStart(), r.getLength(), currentSortProperty, currentSortDesc, new AsyncCall<List<Request>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<Request> result) {
						view.getTable().setData(r.getStart(), r.getLength(), result);
						view.getTable().redraw();
					}
				});
			}
		});
	}

	private void refreshData() {
		requestService.getRequestCount(new AsyncCall<Integer>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(Integer result) {
				view.getTable().setDataSize(result, true);

				if (result < view.getDefaultPageSize()) {
					view.getPager().setVisible(false);
					view.getTable().setPageSize(result);
				}

				view.getTable().refresh();

			}
		});
	}

	private void addColumns() {
		SelectionColumn<Request> selColumn = new SelectionColumn<Request>(view.getSelectionModel());

		view.getTable().addColumn(selColumn);

		final SortableHeader codeHeader = new SortableHeader("Code", "code");
		codeHeader.setSorted(true);

		codeHeader.setUpdater(new ValueUpdater<String>() {

			@Override
			public void update(String value) {
				codeHeader.toggleReverseSort();
				currentSortProperty = codeHeader.getPropertyName();
				currentSortDesc = codeHeader.getReverseSort();
				view.getTable().refresh();
				view.getTable().refreshHeaders();
			}
		});

		view.getTable().addColumn(new TextColumn<Request>() {

			@Override
			public String getValue(Request object) {
				return object.getCode();
			}
		}, codeHeader);

		view.getTable().addColumn(new TextColumn<Request>() {

			@Override
			public String getValue(Request object) {
				return object.getSubject();
			}
		}, "Subject");

		view.getTable().addColumn(new TextColumn<Request>() {

			@SuppressWarnings("deprecation")
			@Override
			public String getValue(Request object) {
				if (object.getDeadline() != null)
					return object.getDeadline().toLocaleString();
				return "";
			}
		}, "Deadline");

		view.getTable().addColumn(new TextColumn<Request>() {

			@Override
			public String getValue(Request object) {
				return object.getState();
			}
		}, "State");

		view.getTable().addColumn(new TextColumn<Request>() {

			@Override
			public String getValue(Request object) {
				return object.getPriority();
			}
		}, "Priority");

		view.getTable().addColumn(new TextColumn<Request>() {

			@Override
			public String getValue(Request object) {
				return object.getRequestor().toString();
			}
		}, "Requestor");

		view.getTable().addColumn(new TextColumn<Request>() {

			@Override
			public String getValue(Request object) {
				if (object.getLabResponsible() != null)
					return object.getLabResponsible().toString();
				return "";
			}
		}, "Lab Resp.");

		Column<Request, String> samplesColumn = new Column<Request, String>(new ButtonCell()) {

			@Override
			public String getValue(Request object) {
				return object.getSamples().size() + " Sample(s)";
			}
		};

		samplesColumn.setFieldUpdater(new FieldUpdater<Request, String>() {

			@Override
			public void update(int index, Request object, String value) {
				eventBus.showRootSampleManagementByRequest(mainWidget, object.getCode());
			}
		});

		view.getTable().addColumn(samplesColumn);

		Column<Request, String> editColumn = new Column<Request, String>(new ButtonCell()) {

			@Override
			public String getValue(Request object) {
				return "Edit";
			}
		};

		editColumn.setFieldUpdater(new FieldUpdater<Request, String>() {

			@Override
			public void update(int index, Request object, String value) {

				List<String> requestorList = new ArrayList<String>();
				int selectedIndex = -1;
				for (Customer c : customerList) {
					requestorList.add(c.toString());
					if (object.getRequestor().toString().equals(c.toString())) {
						selectedIndex = requestorList.size() - 1;
					}
				}
				view.getEditRequestBox().getRequestor().setSelectionItems(requestorList);
				view.getEditRequestBox().getRequestor().getInputBox().setSelectedIndex(selectedIndex);
				view.getEditRequestBox().showEdit(object);

			}
		});

		view.getTable().addColumn(editColumn);

		Column<Request, String> lrColumn = new Column<Request, String>(new ButtonCell()) {

			@Override
			public String getValue(Request object) {
				return "Assign LR";
			}
		};

		lrColumn.setFieldUpdater(new FieldUpdater<Request, String>() {

			@Override
			public void update(int index, Request object, String value) {
				List<String> lrList = new ArrayList<String>();
				int selectedIndex = -1;
				for (LabResponsible lr : labResponsibleList) {
					lrList.add(lr.toString());
					if (object.getLabResponsible() != null) {
						if (object.getLabResponsible().toString().equals(lr.toString())) {
							selectedIndex = lrList.size() - 1;
						}
					}
				}
				view.getAssignLabResponsibleBox().setRequest(object);
				view.getAssignLabResponsibleBox().getLabResponsibleBox().setSelectionItems(lrList);
				view.getAssignLabResponsibleBox().getLabResponsibleBox().getInputBox().setSelectedIndex(selectedIndex);
				view.getAssignLabResponsibleBox().show();
			}
		});

		view.getTable().addColumn(lrColumn);

		Column<Request, String> reportColumn = new Column<Request, String>(new ButtonCell()) {

			@Override
			public String getValue(Request object) {
				return "Reports";
			}
		};

		reportColumn.setFieldUpdater(new FieldUpdater<Request, String>() {

			@Override
			public void update(int index, Request object, String value) {
				view.getEditReportsBox().getLabStaffId().setValue(UserUtil.getLoggedInUser().getId().toString());
				view.getEditReportsBox().getRequestId().setValue(object.getId().toString());
				view.getEditReportsBox().setEditedRequest(object);
				view.getEditReportsBox().getReportTable().refresh();
				view.getEditReportsBox().show();
			}
		});

		view.getTable().addColumn(reportColumn);
	}

	private void bindUIButtons() {
		view.getAddRequestButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {

				List<String> requestorList = new ArrayList<String>();
				for (Customer c : customerList) {
					requestorList.add(c.toString());
				}
				view.getEditRequestBox().getRequestor().setSelectionItems(requestorList);
				view.getEditRequestBox().showNew();
			}
		});

		view.getReleaseRequestsButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				releaseSelectedRequests();
			}
		});

		view.getCloseRequestsButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				closeSelectedRequests();
			}
		});
	}

	private void bindLabResponsibleBox() {
		view.getAssignLabResponsibleBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				assignLabResponsible();
			}
		});

		view.getAssignLabResponsibleBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getAssignLabResponsibleBox().hide();
			}
		});
	}

	private void bindEditBox() {
		view.getEditRequestBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditRequestBox().hide();
			}
		});

		view.getEditRequestBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				if (view.confirmEdit("Do you want to save your changes?"))
					saveRequest();
			}
		});
	}

	private void assignLabResponsible() {
		Request r = view.getAssignLabResponsibleBox().getRequest();
		ListBox lb = view.getAssignLabResponsibleBox().getLabResponsibleBox().getInputBox();

		if (lb.getSelectedIndex() == -1) {
			eventBus.error("Please select a lab responsible!");
			return;
		}

		String selectedLR = lb.getItemText(lb.getSelectedIndex());

		for (LabResponsible lr : labResponsibleList) {
			if (lr.toString().equals(selectedLR)) {
				r.setLabResponsible(lr);

				requestService.saveOrUpdateRequest(r, new AsyncCall<Void>() {

					@Override
					public void onSuccessE(Void arg0) {
						refreshData();
						view.getAssignLabResponsibleBox().hide();
					}

					@Override
					public void onFailureE(Throwable cause) {
						eventBus.error(cause.getMessage());
					}
				});
			}
		}
	}

	private void bindEditReportBox() {
		final CellTable<Report> table = view.getEditReportsBox().getReportTable();

		table.addColumn(new TextColumn<Report>() {

			@Override
			public String getValue(Report object) {
				return object.getWrittenBy().getLastName();
			}
		}, "Written By");

		table.addColumn(new TextColumn<Report>() {

			@Override
			public String getValue(Report object) {
				return DateTimeFormat.getShortDateTimeFormat().format(object.getWrittenAt());
			}
		}, "Written At");

		Column<Report, File> fileNameCol = new Column<Report, File>(new FileDownloadCell()) {

			@Override
			public File getValue(Report object) {
				return (File) object;
			}

		};

		table.addColumn(fileNameCol, "File Name");

		Column<Report, String> deleteCol = new Column<Report, String>(new ButtonCell()) {

			@Override
			public String getValue(Report object) {
				return "X";
			}
		};

		table.addColumn(deleteCol);

		deleteCol.setFieldUpdater(new FieldUpdater<Report, String>() {

			@Override
			public void update(int index, Report object, String value) {
				requestService.deleteReport(object.getId(), new AsyncCall<Void>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(Void result) {
						table.refresh();
					}
				});
			}
		});

		table.setDelegate(new Delegate<Report>() {

			@Override
			public void onRangeChanged(ListView<Report> listView) {

				requestService.getReportsForRequest(view.getEditReportsBox().getEditedRequest(), new AsyncCall<List<Report>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<Report> result) {
						GWT.log(result.size() + "");
						table.setDataSize(result.size(), true);
						table.setData(0, result.size(), result);
						table.redraw();
					}
				});
			}
		});

	}

	private void releaseSelectedRequests() {
		List<Request> requestsToBeReleased = new ArrayList<Request>();

		for (Request r : view.getSelectionModel().getSelectedSet()) {
			requestsToBeReleased.add(r);
		}

		// for (int i = 1; i < view.getTable().getRowCount(); i++) {
		// Request r = (Request) ((TagButton) view.getTable().getWidget(i,
		// 9)).getTag();
		// CheckBox cb = ((CheckBox) view.getTable().getWidget(i, 0));
		// if (cb.getValue() && (r.getState().equals("Not Released") ||
		// r.getState().equals("Information Missing"))) {
		// GWT.log("Addn");
		// requestsToBeReleased.add(r);
		// }
		// cb.setValue(false);
		// }

		if (requestsToBeReleased.size() == 0) {
			eventBus.error("No Requests selected or selected requests could not be released!");
			return;
		}

		requestService.releaseRequests(requestsToBeReleased, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void arg0) {
				refreshData();
			}

			@Override
			public void onFailureE(Throwable cause) {
				eventBus.error(cause.getMessage());
			}
		});
	}

	private void closeSelectedRequests() {
		List<Request> requestsToBeClosed = new ArrayList<Request>();

		for (Request r : view.getSelectionModel().getSelectedSet()) {
			requestsToBeClosed.add(r);
		}

		// for (int i = 1; i < view.getTable().getRowCount(); i++) {
		// Request r = (Request) ((TagButton) view.getTable().getWidget(i,
		// 9)).getTag();
		// CheckBox cb = ((CheckBox) view.getTable().getWidget(i, 0));
		// if (cb.getValue() && (r.getState().equals("Released"))) {
		// requestsToBeClosed.add(r);
		// }
		// cb.setValue(false);
		// }

		if (requestsToBeClosed.size() == 0) {
			eventBus.error("No Requests selected or selected requests could not be closed!");
			return;
		}

		requestService.closeRequests(requestsToBeClosed, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void arg0) {
				refreshData();
			}

			@Override
			public void onFailureE(Throwable cause) {
				eventBus.error(cause.getMessage());
			}
		});
	}

	private void saveRequest() {

		EditRequestBox editBox = view.getEditRequestBox();
		Request tempRequest = editBox.getRequest();

		if (editBox.getRequest() == null)
			tempRequest = new Request();

		tempRequest.setSubject(editBox.getSubject().getValue());
		tempRequest.setDescription(editBox.getDescription().getHTML());

		if (editBox.getState().getInputBox().getSelectedIndex() != -1)
			tempRequest.setState(editBox.getState().getInputBox().getItemText(editBox.getState().getInputBox().getSelectedIndex()));

		if (editBox.getPriority().getInputBox().getSelectedIndex() != -1)
			tempRequest.setPriority(editBox.getPriority().getInputBox().getItemText(editBox.getPriority().getInputBox().getSelectedIndex()));

		if (editBox.getDeadline().getValue() != null)
			tempRequest.setDeadline(editBox.getDeadline().getValue());

		String selectedRequestor = editBox.getRequestor().getInputBox().getItemText(editBox.getRequestor().getInputBox().getSelectedIndex());

		for (Customer c : customerList) {
			if (c.toString().equals(selectedRequestor)) {
				tempRequest.setRequestor(c);
				break;
			}
		}

		Set<InvalidConstraint<Request>> requestErrors = requestValidator.validate(tempRequest);

		if (requestErrors.size() > 0) {
			for (InvalidConstraint<Request> ic : requestErrors) {
				editBox.showError(ic.getItemName(), ic.getMessage());
			}
			return;
		}

		requestService.saveOrUpdateRequest(tempRequest, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {

				view.getEditRequestBox().hide();
				refreshData();
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});
	}

	private void refreshChoices() {

		userService.getAllCustomer(new AsyncCall<List<Customer>>() {

			@Override
			public void onSuccessE(List<Customer> result) {
				customerList = result;
			}

			@Override
			public void onFailureE(Throwable cause) {
				eventBus.error(cause.getMessage());
			}
		});

		userService.getAllLabResponsible(new AsyncCall<List<LabResponsible>>() {

			@Override
			public void onSuccessE(List<LabResponsible> result) {
				labResponsibleList = result;
			}

			@Override
			public void onFailureE(Throwable cause) {
				eventBus.error(cause.getMessage());
			}
		});
	}

	@InjectService
	public void setRequestService(RequestServiceAsync requestService) {
		this.requestService = requestService;
	}

	@InjectService
	public void setUserService(UserServiceAsync userService) {
		this.userService = userService;
	}
}
